<template>
  <div class="image-page">
    <div class="left">
      <material-group
        ref="groupComponent"
        :file-type="1"
        @select-group="handleSelectedGroup"
      ></material-group>
    </div>
    <div class="right">
      <el-row>
        <el-col :span="operation ? 8 : 15">
          <div style="display: flex">
            <el-upload
              ref="uploadRef"
              class="avatar-uploader"
              :auto-upload="false"
              :show-file-list="false"
              multiple
              :on-change="handleFileChange"
              accept="video/*"
            >
              <el-button type="primary">上传视频</el-button>
            </el-upload>

            <el-button
              type="primary"
              :disabled="!selectIds.length"
              @click="batchDel"
              style="margin-left: 10px"
              v-if="operation"
              >批量删除</el-button
            >
            <el-button
              type="primary"
              :disabled="!selectIds.length"
              @click="showBatchMoveGroup = true"
              v-if="operation"
              >批量移动</el-button
            >
          </div>
        </el-col>
        <el-col :span="operation ? 6 : 8">
          <el-input
            v-model="form.fileName"
            placeholder="请输入视频名称"
            clearable
            @blur="handleSearch"
            class="group-search"
          >
            <template #append>
              <el-button :icon="Search" />
            </template>
          </el-input>
        </el-col>
      </el-row>
      <art-table
        :data="tableData"
        :loading="tableLoading"
        :currentPage="form.pageNum"
        :pageSize="form.pageSize"
        :total="pageTotal"
        @row-click="handleSelectedRow"
        @current-change="currentChange"
        @size-change="sizeChange"
        @selection-change="handleSelectionChange"
        :height="850"
      >
        <el-table-column type="selection" v-if="!single" reserve-selection width="55" />
        <el-table-column prop="img" label="视频" width="550">
          <template #default="scope">
            <div class="img-info" v-if="!single">
              <div @click="viewVideo(scope.row.fileUrl)"
                ><video :src="scope.row.fileUrl" class="file-img"></video
              ></div>
              <div class="img-name">
                <el-popover
                  :visible="editVisibleId === scope.row.id"
                  placement="right"
                  :width="400"
                  trigger="click"
                >
                  <template #reference>
                    <p @click="handleEdit(scope.row)" style="cursor: pointer"
                      >{{ scope.row.fileName + scope.row.fileExtension
                      }}<i class="iconfont-sys" style="color: var(--el-color-primary)"
                        >&#xe65f;</i
                      ></p
                    >
                  </template>
                  <template #default>
                    <el-input v-model="newImageName" maxlength="50" show-word-limit></el-input>
                    <div style="margin-top: 10px; text-align: right">
                      <el-button size="small" @click="editVisibleId = null">取消</el-button>
                      <el-button size="small" type="primary" @click="editItemInfo(scope.row)"
                        >确定</el-button
                      >
                    </div>
                  </template>
                </el-popover>
                <p class="img-pixel">{{ formatDuration(scope.row.filePixels) }}</p>
              </div>
            </div>
            <div class="single-info" v-if="single">
              <el-radio-group v-model="selectedId">
                <el-radio :value="scope.row.id"></el-radio>
              </el-radio-group>
              <div>
                <video :src="scope.row.fileUrl" class="file-img"></video>
              </div>
              <div style="margin-left: 10px">
                <p>{{ scope.row.fileName }}</p>
                <p class="img-pixel">{{ formatDuration(scope.row.filePixels) }}</p>
              </div>
            </div>
          </template>
        </el-table-column>
        <el-table-column prop="fileSize" label="大小">
          <template #default="scope">
            {{ formatFileSize(scope.row.fileSize) }}
          </template>
        </el-table-column>
        <el-table-column prop="uploadTime" label="上传时间" width="200" />
        <el-table-column prop="uploadTime" label="操作" v-if="operation">
          <template #default="scope">
            <a
              style="color: var(--el-color-primary); cursor: pointer"
              @click="handleMoveGroup(scope.row)"
              >移动</a
            >
            <el-divider direction="vertical" />
            <a
              style="color: var(--el-color-primary); cursor: pointer"
              @click="copyToClipboard(scope.row.fileUrl)"
              >链接</a
            >
            <el-divider direction="vertical" />
            <a style="color: red; cursor: pointer" @click="handleDel(scope.row.id)">删除</a>
          </template>
        </el-table-column>
      </art-table>
    </div>

    <upload-dialog
      v-if="showImageDialog"
      :file-type="1"
      :active-group="selectedGroup"
      :show-image-dialog-visible="showImageDialog"
      @close-dialog="handleCloseDialog"
    ></upload-dialog>

    <change-group-dialog
      v-if="showMoveGroupDialog"
      :file-type="1"
      :edit-info="editData"
      :show-move-group-dialog-visible="showMoveGroupDialog"
      @close-dialog="handleCloseMoveDialog"
    ></change-group-dialog>

    <batch-move-group-dialog
      v-if="showBatchMoveGroup"
      :ids="selectIds"
      :file-type="1"
      :show-batch-move-group-dialog="showBatchMoveGroup"
      @close-dialog="handleCloseBatchMoveDialog"
    ></batch-move-group-dialog>

    <el-dialog
      title="视频预览"
      v-model="showVideoPreview"
      @close="closePreview"
      :close-on-click-modal="false"
    >
      <video v-if="showVideoPreview" :src="previewUrl" controls width="100%"></video>
    </el-dialog>

    <el-dialog
      v-model="uploadDialogVisible"
      title="视频上传进度"
      width="240px"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      :show-close="false"
      destroy-on-close
      center
    >
      <div style="text-align: center">
        <el-progress
          type="circle"
          :percentage="uploadProgress"
          :status="uploadProgress === 100 ? 'success' : undefined"
        />
      </div>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
  import UploadDialog from './UploadDialog.vue'
  import ChangeGroupDialog from './ChangeGroupDialog.vue'
  import BatchMoveGroupDialog from './BatchMoveGroupDialog.vue'
  import { getFilePage, delFile, editFile, addListFile } from '@/api/materialManagement'
  import { getQiniuToken } from '@/api/ApiList'
  import MaterialGroup from './MaterialGroup.vue'
  import { uploadFileToQiniu } from './qiniuUploader'
  import { Search } from '@element-plus/icons-vue'
  import { ref } from 'vue'

  const props = defineProps({
    single: {
      type: Boolean,
      default: false
    },
    operation: {
      type: Boolean,
      default: true
    }
  })

  const tableData = ref([])
  const showImageDialog = ref(false)

  const emit = defineEmits(['selectVideo'])

  const form = reactive({
    fileType: 1,
    fileName: '',
    groupId: '',
    pageNum: 1,
    pageSize: 10
  })

  const selectedGroup = ref({})
  const tableLoading = ref(false)
  const pageTotal = ref()
  const showMoveGroupDialog = ref(false)
  const editData = ref({})
  const editVisibleId = ref<string | null>(null)
  const newImageName = ref('')
  const selectIds = ref<string[]>([])
  const showBatchMoveGroup = ref(false)
  const groupComponent = ref()
  const showVideoPreview = ref(false)
  const previewUrl = ref('')
  const selectedId = ref()

  interface UploadImageInfo {
    fileName: string
    fileUrl: string
    fileExtension: string
    fileSize: number
    filePixels: string
  }

  interface UploadForm {
    groupId: string | number
    imgList: UploadImageInfo[]
  }

  const formData = reactive<UploadForm>({
    groupId: '',
    imgList: []
  })

  const uploadFileMap = new Map<string, { width: number; height: number }>()
  const uploadLoading = ref(false)
  const uploadDialogVisible = ref(false)
  const uploadProgress = ref(0)
  const submitLoading = ref(false)
  const uploadRef = ref()
  const progressMap = new Map<string, { loaded: number; total: number }>()
  const uploadTimer = ref<number | null>(null)

  function handleSelectedGroup(value: any) {
    selectedGroup.value = value
    form.groupId = value.id === '-1' ? '' : value.id
    formData.groupId = value.id === '-1' ? '' : value.id
    getData()
  }

  function handleCloseDialog(value: boolean) {
    if (value) {
      getData()
    }
    showImageDialog.value = false
  }

  function handleCloseMoveDialog(value: boolean) {
    if (value) {
      getData()
    }
    showMoveGroupDialog.value = false
  }

  const currentChange = (val: number) => {
    form.pageNum = val
    getData()
  }
  const sizeChange = (val: number) => {
    form.pageSize = val
    form.pageNum = 1
    getData()
  }

  function handleSearch() {
    form.pageNum = 1
    form.pageSize = 10
    getData()
  }

  function handleSelectedRow(row: any) {
    selectedId.value = row.id
    emit('selectVideo', row)
  }

  function handleDel(id: string) {
    ElMessageBox.confirm('确定要删除吗？', '提示', {
      type: 'warning',
      confirmButtonText: '确定',
      cancelButtonText: '取消'
    }).then(() => {
      delImg(id)
    })
  }

  function batchDel() {
    ElMessageBox.confirm('确定批量删除吗？', '提示', {
      type: 'warning',
      confirmButtonText: '确定',
      cancelButtonText: '取消'
    }).then(() => {
      let ids = selectIds.value.join(',')
      delImg(ids)
    })
  }

  function copyToClipboard(text: string) {
    navigator.clipboard
      .writeText(text)
      .then(() => {
        ElMessage.success('复制成功')
      })
      .catch(() => {
        ElMessage.error('复制失败，请手动复制')
      })
  }

  function formatDuration(seconds: string): string {
    const newSeconds = Number(seconds)
    const hrs = Math.floor(newSeconds / 3600)
    const mins = Math.floor((newSeconds % 3600) / 60)
    const secs = Math.floor(newSeconds % 60)

    const pad = (n: number) => n.toString().padStart(2, '0')
    return `${pad(hrs)}:${pad(mins)}:${pad(secs)}`
  }

  function handleSelectionChange(data: any[]) {
    selectIds.value = data.map((item) => item.id)
    emit('selectVideo', data)
  }

  async function delImg(id: string) {
    const res = await delFile(id)
    if (res.code === '00000') {
      await getData()
      ElMessage.success('删除成功')
    }
  }

  function handleMoveGroup(item: any) {
    showMoveGroupDialog.value = true
    editData.value = JSON.parse(JSON.stringify(item))
  }

  function handleEdit(item: any) {
    editVisibleId.value = item.id

    const fileName = item.fileName
    // const dotIndex = fileName.lastIndexOf('.')
    // newImageName.value = dotIndex !== -1 ? fileName.substring(0, dotIndex) : fileName
    newImageName.value = fileName
  }

  function handleCloseBatchMoveDialog(value: boolean) {
    if (value) {
      getData()
    }
    showBatchMoveGroup.value = false
  }

  async function editItemInfo(item: any) {
    // const oldName = item.fileName
    // const ext = oldName.includes('.') ? '.' + oldName.split('.').pop() : ''
    // const newName = newImageName.value + ext
    const newName = newImageName.value

    const res = await editFile(item.id, {
      id: item.id,
      fileName: newName
    })
    if (res.code === '00000') {
      editVisibleId.value = null
      await getData()
      ElMessage.success('修改成功')
    }
  }

  function formatFileSize(size: string): string {
    let newSize = Number(size)
    if (newSize < 1024) {
      return newSize + ' B'
    } else if (newSize < 1024 * 1024) {
      return (newSize / 1024).toFixed(2) + ' KB'
    } else {
      return (newSize / 1024 / 1024).toFixed(2) + ' MB'
    }
  }

  function viewVideo(url: string) {
    showVideoPreview.value = true
    previewUrl.value = url
  }

  function closePreview() {
    showVideoPreview.value = false
    previewUrl.value = ''
  }

  async function getData() {
    try {
      tableLoading.value = true
      const res = await getFilePage(form)
      if (res.code === '00000') {
        tableData.value = res.data.list
        pageTotal.value = res.data.total

        groupComponent.value.getData()
      }
    } catch (e) {
    } finally {
      tableLoading.value = false
    }
  }

  async function handleFileChange(uploadFile: any, uploadFiles: any) {
    if (uploadFile.status !== 'ready') {
      return
    }

    const file = uploadFile.raw
    const isVideo = file.type === 'video/mp4'
    const isLt500M = file.size / 1024 / 1024 < 500
    if (!isVideo) {
      ElMessage.error('视频格式仅支持 MP4')
      return false
    }
    if (!isLt500M) {
      ElMessage.error('视频大小不能超过 500MB!')
      return false
    }

    try {
      const duration = await getVideoDuration(file)
      uploadFileMap.set(file.uid, { width: duration, height: 0 })
    } catch {
      ElMessage.error('读取视频时长失败')
    }

    if (uploadLoading.value) {
      ElMessage.warning('当前有文件正在上传，新添加的文件已被忽略。')
      uploadRef.value?.handleRemove(uploadFile)
      return
    }

    if (uploadTimer.value !== null) {
      clearTimeout(uploadTimer.value)
    }

    uploadTimer.value = window.setTimeout(() => {
      processUploadQueue(uploadFiles)
    }, 50)
  }

  async function processUploadQueue(uploadFiles: any) {
    const filesToUpload = uploadFiles.filter((f: any) => f.status === 'ready')

    if (filesToUpload.length === 0) {
      return
    }

    uploadLoading.value = true
    uploadDialogVisible.value = true
    uploadProgress.value = 0
    progressMap.clear()

    let token: string = ''
    let batchTotalSize = 0

    try {
      const tokenResult = await getQiniuToken({})
      token = tokenResult.data
      if (!token) throw new Error('获取上传凭证失败')

      batchTotalSize = filesToUpload.reduce((sum: any, f: any) => sum + (f.size || 0), 0)
      filesToUpload.forEach((f: any) => {
        progressMap.set(f.uid, { loaded: 0, total: f.size || 1 })
      })

      const uploadPromises = filesToUpload.map((f: any) => {
        f.status = 'uploading'
        const file = f.raw

        const onProgress = (percent: number) => {
          const progressData = progressMap.get(f.uid)
          if (progressData) {
            progressData.loaded = (progressData.total * percent) / 100
            updateTotalProgress(batchTotalSize)
          }
        }

        return uploadFileToQiniu(file, token, onProgress)
          .then((result) => ({
            ...result,
            originalFile: file,
            uid: f.uid,
            success: true
          }))
          .catch((reason) => {
            return {
              originalFile: file,
              uid: f.uid,
              success: false,
              reason: reason
            }
          })
      })

      const results = await Promise.all(uploadPromises)
      uploadProgress.value = 100

      const successfulUploads: any[] = []
      let failedCount = 0

      results.forEach((res) => {
        const fileItem = uploadFiles.find((f: any) => f.uid === res.uid)

        if (res.success) {
          fileItem.status = 'success'
          const sizeInfo: any = uploadFileMap.get(res.uid)
          let filePixels = `${Math.round(sizeInfo.width)}`
          successfulUploads.push({
            fileName: res.originalFile.name,
            fileUrl: res.url,
            fileExtension: `.${res.originalFile.name.split('.').pop()}`,
            filePixels,
            fileSize: res.originalFile.size
          })
        } else {
          fileItem.status = 'fail'
          failedCount++
        }
      })

      if (successfulUploads.length > 0) {
        formData.imgList.push(...successfulUploads)
        await submit()
      } else if (failedCount > 0) {
        ElMessage.error(`文件上传失败，未提交数据。`)
      }
    } catch (error: any) {
      ElMessage.error(error.message || '错误')
      filesToUpload.forEach((f: any) => {
        if (f.status === 'uploading') f.status = 'fail'
      })
    } finally {
      uploadLoading.value = false
      uploadTimer.value = null
      setTimeout(() => {
        uploadDialogVisible.value = false
        uploadRef.value?.clearFiles()
      }, 800)
    }
  }

  function updateTotalProgress(batchTotalSize: number) {
    if (batchTotalSize === 0) return
    let batchLoadedSize = 0
    progressMap.forEach((p) => {
      batchLoadedSize += p.loaded
    })
    uploadProgress.value = Math.floor((batchLoadedSize / batchTotalSize) * 100)
  }

  async function submit() {
    try {
      if (!formData.groupId) {
        formData.groupId = groupComponent.value.getDefaultGroupId()
      }
      submitLoading.value = true
      const res = await addListFile({
        fileType: 1,
        groupId: formData.groupId,
        fileVos: formData.imgList
      })
      if (res.code === '00000') {
        ElMessage.success('添加成功')
        formData.imgList = []
        getData()
      }
    } catch (e) {
    } finally {
      submitLoading.value = false
    }
  }

  function getVideoDuration(file: File): Promise<number> {
    return new Promise((resolve, reject) => {
      const video = document.createElement('video')
      video.preload = 'metadata'
      video.onloadedmetadata = () => {
        URL.revokeObjectURL(video.src)
        resolve(video.duration)
      }
      video.onerror = () => reject()
      video.src = URL.createObjectURL(file)
    })
  }

  onMounted(() => {
    getData()
  })
</script>

<style scoped lang="scss">
  .image-page {
    display: flex;

    .left {
      width: 300px;
    }

    .right {
      flex: 1;
      height: 100%;
      overflow-x: auto;
    }

    .img-info {
      display: flex;
    }

    .single-info {
      display: flex;
      align-items: center;
    }

    .img-name {
      margin-left: 6px;
    }

    .file-img {
      width: 120px;
      height: 60px;
      cursor: pointer;
    }
  }

  .img-pixel {
    margin-top: 10px;
    color: #ccc;
  }
</style>
